The DataTable, TableConfigPanel, and supporting components (DataExporter, ColumnGroup, Row, Column, ExpansionTogger, etc.) were designed with certain assumptions, based on dealing with certain use cases.

Case Z:  Simplest case, with only body columns.

<ace:dataTable>
    <ace:column id="a" headerText="a">
        …
    </ace:column>
    <ace:column id="b" headerText="b" sortBy="…">
        …
    </ace:column>
    <ace:column id="c" headerText="c">
        …
    </ace:column>
</ace:dataTable>

Case Y:  Both header and body columns, one-to-one correspondence between header and body columns, so that body columns inherit properties from header columns. All header columns are bottom reaching.

<ace:dataTable>
    <ace:columnGroup type="header">
        <ace:row>
            <ace:column id="a_head" headerText="a">
                …
            </ace:column>
            <ace:column id="b_head" headerText="b" sortBy="…">
                …
            </ace:column>
            <ace:column id="c_head" headerText="c">
                …
            </ace:column>
        </ace:row>
    </ace:columnGroup>

    <ace:column id="a_body">
        …
    </ace:column>
    <ace:column id="b_body">
        …
    </ace:column>
    <ace:column id="c_body">
        …
    </ace:column>
</ace:dataTable>

Case X:  Multiple levels of header columns, imparting hierarchy of categories and sub-categories of columns. Bottom reaching header columns have one-to-one correspondence with body columns, so that body columns inherit properties from header columns.

<ace:dataTable>
    <ace:columnGroup type="header">
        <ace:row>
            <ace:column id="category_special" headerText="special" colspan="2">
                …
            </ace:column>
            <ace:column id="c_head" headerText="c" rowspan="2">
                …
            </ace:column>
        </ace:row>

        <ace:row>
            <ace:column id="a_head" headerText="a">
                …
            </ace:column>
            <ace:column id="b_head" headerText="b" sortBy="…">
                …
            </ace:column>
        </ace:row>
    </ace:columnGroup>

    <ace:column id="a_body">
        …
    </ace:column>
    <ace:column id="b_body">
        …
    </ace:column>
    <ace:column id="c_body">
        …
    </ace:column>
</ace:dataTable>

In each of those cases, we have the following assumptions:
- Body columns can exist on their own.
- If there are header columns, then every body column has a corresponding bottom reaching header column from which it inherits properties, and is re-ordered along with in the TableConfigPanel.
- Non-bottom-reaching header columns are for organising and categorising the header columns below them.

From the feedback we’ve received from Williams, we’ve identified 3 cases that involve dynamic rendering on a row-by-row basis, of which 2 cases violate some of the above mentioned assumptions, for which there is no means of inferring a solution from the information available to the code as it is now, necessitating a new property on the column component, to provide the necessary information to function properly.

There may exist other unidentified use cases that further complicate things, which is why it’s important to communicate these with Williams, so that they can provide any other possible use cases that need to be addressed.

Case A:  Not a problem. Body column has children within it, some of which are conditionally rendered for some rows, other children rendered for other rows. Different row types might be parent / expanded children, or might just be different types of row objects. There exists one header column for every one body column. Total header column span matches total body column span.  Might be a special case of case X or case Y.

<ace:dataTable var="tableRowVar">
    <ace:columnGroup type="header">
        <ace:row>
            <ace:column id="a_head" headerText="a">
                …
            </ace:column>
            <ace:column id="b_head" headerText="b" sortBy="…">
                …
            </ace:column>
            <ace:column id="c_head" headerText="c">
                …
            </ace:column>
        </ace:row>
    </ace:columnGroup>

    <ace:column id="a_body">
        …
    </ace:column>
    <ace:column id="b_body">
        <h:panelGroup id="b_body_parent" rendered="#{tableRowVar.parent}">
            …
        </h:panelGroup>
        <h:panelGroup id="b_body_child" rendered="#{not tableRowVar.parent}">
            …
        </h:panelGroup>
    </ace:column>
    <ace:column id="c_body">
        …
    </ace:column>
</ace:dataTable>

Case B:  Problem. Multiple levels of header columns do not impart hierarchy of categories, but rather are used for parent header and child header.  There are multiple body columns that each vie to render in the same column position, depending on their rendered property, used in different body rows, parent row or child row.

Problems:
- There is no special marker on the top header column to indicate that this is not case X, and that the top header column is not a category, but should instead be treated like a bottom reaching header column, that has the potential to correspond to a body column, for inheriting properties.
- Also means that the TableConfigPanel won’t know that re-ordering the non-bottom-reaching header column should also result in a body column being moved with it, since they’re not corresponded. Re-ordering columns will likely corrupt the view.
- There is no special marker on the subsequent body column, that’s vying to display in the same column position, to indicate that this is not case X or Y, it should not correspond to the subsequent header column to the right, but should in fact correspond to one of the two header columns that are to the left of it, on the upper or lower header rows. Just because it has an EL expression for its rendered property isn’t useful, the old code didn’t inherit properties so rendered had to be set on both the header and body columns.
- We have enough information to render the body column in the correct position, now that we are more precise in using the column rendered property, but there is not enough information to know to which header column to correspond and inherit properties from. This causes a skew, where every body column afterwards is mis-corresponded, and mis-inheriting properties.

Solution:
- Add a for property to the body column to identify which header column is corresponds to. That way the initial body column (a_body_parent) can indicate which of the upper (a_head_parent) or lower (a_head_child) header columns it corresponds to, and the subsequent body column (a_body_child) can indicate that it does not correspond to the subsequent header column to the right (b_head_child), but should in fact correspond to one of the two header columns that are to the left of it (a_head_child).
- Re-work the correspondence algorithm to use the column positions in conjunction with the new for property.

<ace:dataTable var="tableRowVar">
    <ace:columnGroup type="header">
        <ace:row>
            <ace:column id="a_head_parent" headerText="a parent">
                …
            </ace:column>
            <ace:column id="b_head_parent" headerText="b parent" sortBy="…">
                …
            </ace:column>
            <ace:column id="c_head_parent" headerText="c parent">
                …
            </ace:column>
        </ace:row>

        <ace:row>
            <ace:column id="a_head_child" headerText="a child">
                …
            </ace:column>
            <ace:column id="b_head_child" headerText="b child">
                …
            </ace:column>
            <ace:column id="c_head_child" headerText="c child"  sortBy="…">
                …
            </ace:column>
        </ace:row>
    </ace:columnGroup>

    <ace:column id="a_body_parent" rendered="#{tableRowVar.parent}" for="a_head_parent">
        …
    </ace:column>
    <ace:column id="a_body_child" rendered="#{not tableRowVar.parent}" for="a_head_child">
        …
    </ace:column>
    <ace:column id="b_body_parent" rendered="#{tableRowVar.parent}" for="b_head_parent">
        …
    </ace:column>
    <ace:column id="b_body_child" rendered="#{not tableRowVar.parent}" for="b_head_child">
        …
    </ace:column>
    <ace:column id="c_body_parent" rendered="#{tableRowVar.parent}" for="c_head_parent">
        …
    </ace:column>
    <ace:column id="c_body_child" rendered="#{not tableRowVar.parent}" for="c_head_child">
        …
    </ace:column>
</ace:dataTable>

Case C:  Problem. Simplified version of case B.  Either only one row of header columns, or any level above is in fact following the assumption of hierarchy of categories.  But there are still multiple body columns that each vie to render in the same column position, depending on their rendered property, used in different body rows, parent row or child row.

Problems:
- This breaks the assumption of a one-to-one correspondence between a header column and a body column.
- TableConfigPanel does not currently support re-ordering one header column and having multiple body columns move with it. Re-ordering columns will likely corrupt the view.
- There is no special marker on the subsequent body column, that’s vying to display in the same column position, to indicate that this is not case X or Y, it should not correspond to the subsequent header column to the right, but should in fact correspond to the header column to the left of it. Just because it has an EL expression for its rendered property isn’t useful, the old code didn’t inherit properties so rendered had to be set on both the header and body columns.
- We have enough information to render the body column in the correct position, now that we are more precise in using the column rendered property, but there is not enough information to know to which header column to correspond and inherit properties from. This causes a skew, where every body column afterwards is mis-corresponded, and mis-inheriting properties.
- We’ve only found one case of this so far, where the ExpansionToggler exists in one column and so in some rows, but not in the other column, and so not in other rows. In theory this could easily be changed to be case A, but an audit of the code shows that ExpansionToggler.rendered property is not used when rendering or exporting the DataTable, likely forcing the use of case C.

Solution:
- Add a for property to the body column to identify which header column is corresponds to. That way the subsequent body column (a_body_child) can indicate that it does not correspond to the subsequent header column to the right (b_head), but should in fact correspond to the header column to the left of it (a_head).
- Re-work the correspondence algorithm to use the column positions in conjunction with the new for property.
- Re-work the TableConfigPanel to allow for multiple body columns to be re-ordered by a header column.
- ExpansionToggler would be re-worked so that it’s rendered property would be used, and evaluated per-row. In the existing Export code, it is evaluated up-front, before row iteration, so can’t be tied to the DataTable’s rowVar.
- Possibly other aspects of exporting will need to be brought in line with how columns are rendered.

<ace:dataTable var="tableRowVar">
    <ace:columnGroup type="header">
        <ace:row>
            <ace:column id="a_head" headerText="a">
                …
            </ace:column>
            <ace:column id="b_head" headerText="b" sortBy="…">
                …
            </ace:column>
            <ace:column id="c_head" headerText="c">
                …
            </ace:column>
        </ace:row>
    </ace:columnGroup>

    <ace:column id="a_body_parent" rendered="#{tableRowVar.parent}" for="a_head">
        …
    </ace:column>
    <ace:column id="a_body_child" rendered="#{not tableRowVar.parent}" for="a_head">
        …
    </ace:column>
    <ace:column id="b_body_parent" rendered="#{tableRowVar.parent}" for="b_head">
        …
    </ace:column>
    <ace:column id="b_body_child" rendered="#{not tableRowVar.parent}" for="b_head">
        …
    </ace:column>
    <ace:column id="c_body">
        …
    </ace:column>
</ace:dataTable>

=================

1. Find out if there are other cases than the ones identified, and if they impose other restrictions or necessitate further changes.

2. Build tests for B, C  (3 hours)

3. Add the for property on the body column, and re-work ColumnGroupModel, Iterator, Traverser, all uses of the Iterator and Traverser, and the correspondence algorithm.  (2 days)

3. Re-work re-ordering to handle multiple body columns per header column. TableConfigPanel rendering, javascript, DataTableDecoder.  (4 hours)

4. Optional:  Re-work ExpansionToggler use.  Test rendering and exporting.  (2 days)

5. Test for regressions in cases X,Y,Z. Test new cases B, C.  (1 day)

6. DOM exception, ICE-9692.